from django.http import HttpResponse
from django.shortcuts import render_to_response, redirect
from django.core.exceptions import ObjectDoesNotExist
from django.utils import simplejson

from pyfacture import models
from pyfacture import message
from pyfacture.pdf.TimesheetPDFCreator import TimesheetPDFCreator

import builder
import utils

import datetime
import json
import logging
import uuid

__author__ = 'Pierre Moermans'

logger = logging.getLogger(__name__)

class TimesheetDecoder(json.JSONDecoder):
    def decode(self, s):
        timesheet_dict = json.loads(s)
        timesheet = models.Timesheet()
        timesheet.id = timesheet_dict['id']
        if timesheet.id == '':
            timesheet.id = None
        timesheet.project = timesheet_dict['project']
        timesheet.contract = models.Contract.objects.get(id=timesheet_dict['contract'])
        timesheet.manager = timesheet_dict['manager']
        timesheet.consultant = timesheet_dict['consultant']
        days = []
        worked_hours = 0
        for json_day in timesheet_dict['days']:
            day = models.Day()
            day.date = datetime.date(int(json_day['date'][:4]), int(json_day['date'][5:7]), int(json_day['date'][8:10]))
            day.worked_hours = json_day['workedStdHours']
            
            day.timesheet = timesheet
            days.append(day)

        logger.debug('Decoded timesheet ' + str(timesheet))
        return timesheet, days

def edit(request, timesheet_id):

    if not request.user.is_authenticated():
        return render_to_response('logon.html', {'original_url': request.path})
    timesheet = models.Timesheet.objects.get(id=timesheet_id)

    if timesheet.user.id != request.user.id:
        return view_default(request)

    if timesheet is None:
        return new_default(request)

    days = builder.buildDaysForTimesheet(list(timesheet.day_set.all()), [])
    _set_holidays(days)

    monthAndYear = days[10].date.strftime('%m%Y')
    for day in days:
        day.date = day.date.strftime('%d/%m')

    try:
        month_before = 2
        month_after = 4
    except models.Day.DoesNotExist:
        month_before = 3
        month_after = 3

    try:
        all_contracts = list(models.Contract.objects.all())
    except models.Customer.DoesNotExist:
        all_contracts = []

    return render_to_response("new_timesheet.html",
                              {'selectedMonthAndYear': monthAndYear,
                               'availableMonths': utils.getMonthsBeforeAndAfterForTemplate(month_before, month_after, timesheet),
                               'consultant': timesheet.user.first_name + ' ' + timesheet.user.last_name,
                               'contracts': utils.getContractsForTemplate(all_contracts),
                               'selectedContract': timesheet.contract.id,
                               'project': 'E-Tracker',
                               'manager': 'Elodie Doncq',
                               'days': days,
                               'year': '2011',
                               'active_menu': 'timesheet',
                               'active_submenu': 'new',
                               'new_link': '/timesheet/new/',
                               'view_link': '/timesheet/view/',
                               'timesheet_id': timesheet.id})

def new(request, monthAndYear):
    if not request.user.is_authenticated():
        return render_to_response('logon.html', {'original_url': request.path})
    first_day_of_month = utils.getFirstDayOfMonth(monthAndYear)
    days = builder.buildDaysForMonth(first_day_of_month, [])
    _set_holidays(days, holidays_not_worked = True)

    for day in days:
        day.date = day.date.strftime('%d/%m')

    month_before = 12
    month_after = 2

    project = ""
    manager = ""
    try:
        latest_day = models.Day.objects.latest('date')
        latest_timesheet = latest_day.timesheet
        project = latest_timesheet.project
        manager = latest_timesheet.manager
    except:
        pass

    try:
        all_contracts = list(models.Contract.objects.all())
    except models.Contract.DoesNotExist:
        all_contracts = []

    return render_to_response("new_timesheet.html",
                              {'selectedMonthAndYear': monthAndYear,
                               'availableMonths': utils.getMonthsBeforeAndAfterForTemplate(month_before, month_after, None),
                               'consultant': request.user.first_name + ' ' + request.user.last_name,
                               'contracts': utils.getContractsForTemplate(all_contracts),
                               'project': project,
                               'manager': manager,
                               'days': days,
                               'year': '2011',
                               'active_menu': 'timesheet',
                               'active_submenu': 'new',
                               'new_link': '/timesheet/new/',
                               'view_link': '/timesheet/view/'})

def new_default(request):
    if not request.user.is_authenticated():
        return render_to_response('logon.html', {'original_url': request.path})

    today = datetime.datetime.now()
    return new(request, today.strftime('%m%Y'))

def _set_holidays(days, holidays_not_worked = False):
    holidays = models.Holiday.objects.filter(date__gte=days[0].date).filter(date__lte=days[-1].date);
    holidays_dates = []
    for holiday in holidays:
        holidays_dates.append(holiday.date)

    for day in days:
        if datetime.datetime(day.date.year, day.date.month, day.date.day) in holidays_dates:
            day.holiday = True
            if holidays_not_worked:
                day.worked_hours = 0
        else:
            day.holiday = False

def _generate_view(request, timesheet, error_msg = ''):
    days = timesheet.day_set.all()
    days = builder.buildDaysForTimesheet(list(days), [])

    _set_holidays(days)

    for day in days:
        day.date = day.date.strftime('%d/%m')

    availableTimesheets = models.Timesheet.objects.filter(user__id = request.user.id)

    if error_msg == '':
        error_msg = None

    return render_to_response("view_timesheet.html",
                              {'selected_id': timesheet.id,
                               'availableTimesheets': utils.getTimesheetsForTemplate(availableTimesheets),
                               'consultant': request.user.first_name + ' ' + request.user.last_name,
                               'customer': timesheet.getName(),
                               'contract': timesheet.getContract(),
                               'project': timesheet.project,
                               'manager': timesheet.manager,
                               'days': days,
                               'active_menu': 'timesheet',
                               'active_submenu': 'view',
                               'new_link': '/timesheet/new/',
                               'view_link': '/timesheet/view/',
                               'error_msg': error_msg})

def view(request, timesheet_id):
    logger.debug("User requested to view timesheet " + timesheet_id)
    if not request.user.is_authenticated():
        return render_to_response('logon.html', {'original_url': request.path})
    timesheet = models.Timesheet.objects.get(id=timesheet_id)

    if timesheet.user.id != request.user.id:
        logger.info(request.user.username + ' tried to see the timesheet ' + str(timesheet) + " which he doesn't own" )
        return view_default(request)

    if timesheet is None:
        latest_day = models.Day.objects.latest('date')
        return _generate_view(request, latest_day.timesheet, "No such timesheet")

    return _generate_view(request, timesheet)

def view_default(request):
    if not request.user.is_authenticated():
        return render_to_response('logon.html', {'original_url': request.path})

    try:
        latest_day = models.Day.objects.filter(timesheet__user__id = request.user.id).latest('date')
    except ObjectDoesNotExist:
        return new_default(request)

    return _generate_view(request, latest_day.timesheet)

def pdf(request, timesheet_id):
    if not request.user.is_authenticated():
        return render_to_response('logon.html', {'original_url': request.path})

    timesheet = models.Timesheet.objects.get(id=timesheet_id)
    if timesheet is None:
        latest_day = models.Day.objects.latest('date')
        return _generate_view(request, latest_day.timesheet, "No such timesheet")
    
    response = HttpResponse(mimetype='application/pdf')
    days = list(timesheet.day_set.all())
    response['Content-Disposition'] = 'attachment; filename=timesheet-' + days[0].date.strftime('%Y%m') + '.pdf'

    period_string = days[0].date.strftime('%m / %Y')
    days_for_timesheet = builder.buildDaysForTimesheet(list(days), [])
    _set_holidays(days_for_timesheet)
    pdfCreator = TimesheetPDFCreator(response)
    pdfCreator.setHeader(consultant="Pierre Moermans", period=period_string, project=timesheet.project,
                         manager=timesheet.manager)

    def getDate(day):
        return (day.date, day.worked_hours, day.part_of_invoice, day.holiday)

    i = 0
    while 7 * i < len(days_for_timesheet):
        week_number = days_for_timesheet[i * 7 + 1].date.isocalendar()[1]
        pdfCreator.addWeek(week_number, map(getDate, days_for_timesheet[i * 7:i * 7 + 7]))
        i += 1

    pdfCreator.addSignatureBoxes(consultant="Pierre Moermans", manager=timesheet.manager)
    pdfCreator.save()
    return response

def save(request):
    logger.debug("User wants to save a new timesheet")
    if not request.user.is_authenticated():
        logger.debug("User wants to save a new timesheet but is not authenticated")
        return redirect('/loginform/', {'original_url': request.path})
    if request.is_ajax():
        try:
            timesheet, days = TimesheetDecoder().decode(request.raw_post_data)
            timesheet.user = request.user

            if timesheet.user.first_name is None or timesheet.user.first_name == '':
                uid = uuid.uuid4()
                logger.error(message.msgWithUUID(uid,"The user first name is not set using the user configuration"))
                return message.buildError("Configuration issue", uid)
            if timesheet.user.last_name is None or timesheet.user.last_name == '':
                uid = uuid.uuid4()
                logger.error(message.msgWithUUID(uid,"The user last name is not set using the user configuration"))
                return message.buildError("Configuration issue", uid)

            timesheet.save()
            timesheet.day_set.all().delete()
            for day in days:
                timesheet.day_set.add(day)
            logger.info("Timesheet " + str(timesheet) + " has been saved")
        except Exception, e:
            uid = uuid.uuid4()
            logger.error(message.msgWithUUID(uid, "Error saving timesheet"), e)
            return message.buildError("Error saving timesheet", uid)
        redirect_url = "../../timesheet/view/" + str(timesheet.id) + "/"
        return HttpResponse('{"redirect":"' + redirect_url + '"}', 'application/json')

    now = datetime.datetime.now()
    redirect_url = "../../timesheet/new/"
    return HttpResponse(redirect_url, 'text/html')

#todo return errors as JSON object
def forInvoiceLine(request, timesheet_id):
    logger.debug("User wants to get timesheet " + str(timesheet_id) + " as invoice line")
    if not request.user.is_authenticated():
        logger.debug("User wants to get timesheet " + str(timesheet_id) + " as invoice line but is not authenticated")
        return redirect('/loginform/', {'original_url': request.path})

    try:
        try:
            timesheet = models.Timesheet.objects.get(id=int(timesheet_id))
            if not timesheet.contract:
                logger.warn("Timesheet " + str(timesheet_id) + " cannot be obtained as invoice line because there is no contract associated to it")
            sample_date = timesheet.day_set.all()[10].date
            description = "Hours of consultancy for " + timesheet.contract.customer.name + ", " + str(sample_date.month) + "/" + str(sample_date.year)
            unit_price = timesheet.contract.hourly_rate
            vat = 21
            quantity = timesheet.getTotalHours()

            data = simplejson.dumps({'description': description, 'unit_price': unit_price, 'vat': vat, 'quantity': quantity})
            return HttpResponse(data,'application/javascript')
        except models.DoesNotExist, e:
            logger.warn("User wanted to get timesheet " + str(timesheet_id) + " as invoice line but it doesn't exist")
            return message.buildWarning("No such timesheet")
    except Exception, e:
        uid = uuid.uuid4()
        logger.error(message.msgWithUUID(uid,"Error getting timesheet " + str(timesheet_id) + " as invoice line"), e)
        return message.buildError("Error getting timesheet", uid)
